package cn.iocoder.yudao.module.lowcode.core.naming;

import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.lowcode.enums.NamingConventionEnum;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.regex.Pattern;

/**
 * 字段命名转换器
 * 
 * 支持多种命名格式之间的相互转换，包括智能检测和自定义规则
 *
 * @author 芋道源码
 */
@Slf4j
public class NamingConverter {

    // 缓存转换结果，提高性能
    private static final Map<String, String> conversionCache = new HashMap<>();
    
    // 命名模式正则表达式
    private static final Pattern SNAKE_CASE_PATTERN = Pattern.compile("^[a-z]+(_[a-z0-9]+)*$");
    private static final Pattern UPPER_SNAKE_CASE_PATTERN = Pattern.compile("^[A-Z]+(_[A-Z0-9]+)*$");
    private static final Pattern CAMEL_CASE_PATTERN = Pattern.compile("^[a-z][a-zA-Z0-9]*$");
    private static final Pattern PASCAL_CASE_PATTERN = Pattern.compile("^[A-Z][a-zA-Z0-9]*$");
    private static final Pattern KEBAB_CASE_PATTERN = Pattern.compile("^[a-z]+(-[a-z0-9]+)*$");

    /**
     * 将字段名从源格式转换为目标格式
     */
    public static String convert(String fieldName, NamingConventionEnum fromFormat, NamingConventionEnum toFormat) {
        if (StrUtil.isEmpty(fieldName) || fromFormat == toFormat) {
            return fieldName;
        }

        // 检查缓存
        String cacheKey = fieldName + ":" + fromFormat.getCode() + ":" + toFormat.getCode();
        if (conversionCache.containsKey(cacheKey)) {
            return conversionCache.get(cacheKey);
        }

        try {
            // 首先标准化为统一格式（使用下划线分隔的小写格式作为中间格式）
            String normalized = normalizeToStandard(fieldName, fromFormat);
            
            // 从标准格式转换为目标格式
            String result = convertFromStandard(normalized, toFormat);
            
            // 缓存结果
            conversionCache.put(cacheKey, result);
            
            return result;
            
        } catch (Exception e) {
            log.warn("[NamingConverter] 字段名转换失败: fieldName={}, from={}, to={}", 
                    fieldName, fromFormat.getCode(), toFormat.getCode(), e);
            return fieldName; // 转换失败时返回原字段名
        }
    }

    /**
     * 智能检测字段命名格式
     */
    public static NamingConventionEnum detectNamingConvention(String fieldName) {
        if (StrUtil.isEmpty(fieldName)) {
            return NamingConventionEnum.SNAKE_CASE;
        }

        // 按优先级检测
        if (SNAKE_CASE_PATTERN.matcher(fieldName).matches()) {
            return NamingConventionEnum.SNAKE_CASE;
        }
        if (UPPER_SNAKE_CASE_PATTERN.matcher(fieldName).matches()) {
            return NamingConventionEnum.UPPER_SNAKE_CASE;
        }
        if (CAMEL_CASE_PATTERN.matcher(fieldName).matches()) {
            return NamingConventionEnum.CAMEL_CASE;
        }
        if (PASCAL_CASE_PATTERN.matcher(fieldName).matches()) {
            return NamingConventionEnum.PASCAL_CASE;
        }
        if (KEBAB_CASE_PATTERN.matcher(fieldName).matches()) {
            return NamingConventionEnum.KEBAB_CASE;
        }
        
        // 检查是否为全小写或全大写
        if (fieldName.equals(fieldName.toLowerCase()) && !fieldName.contains("_") && !fieldName.contains("-")) {
            return NamingConventionEnum.LOWER_CASE;
        }
        if (fieldName.equals(fieldName.toUpperCase()) && !fieldName.contains("_") && !fieldName.contains("-")) {
            return NamingConventionEnum.UPPER_CASE;
        }

        // 默认返回snake_case
        return NamingConventionEnum.SNAKE_CASE;
    }

    /**
     * 批量检测表中字段的命名格式
     */
    public static NamingConventionEnum detectTableNamingConvention(List<String> fieldNames) {
        if (fieldNames == null || fieldNames.isEmpty()) {
            return NamingConventionEnum.SNAKE_CASE;
        }

        Map<NamingConventionEnum, Integer> conventionCounts = new HashMap<>();
        
        for (String fieldName : fieldNames) {
            NamingConventionEnum detected = detectNamingConvention(fieldName);
            conventionCounts.merge(detected, 1, Integer::sum);
        }

        // 返回出现次数最多的命名格式
        return conventionCounts.entrySet().stream()
                .max(Map.Entry.comparingByValue())
                .map(Map.Entry::getKey)
                .orElse(NamingConventionEnum.SNAKE_CASE);
    }

    /**
     * 将各种格式标准化为下划线分隔的小写格式
     */
    private static String normalizeToStandard(String fieldName, NamingConventionEnum fromFormat) {
        switch (fromFormat) {
            case SNAKE_CASE:
                return fieldName.toLowerCase();
            case UPPER_SNAKE_CASE:
                return fieldName.toLowerCase();
            case CAMEL_CASE:
            case PASCAL_CASE:
                return camelToSnake(fieldName);
            case KEBAB_CASE:
                return fieldName.toLowerCase().replace("-", "_");
            case LOWER_CASE:
                return fieldName;
            case UPPER_CASE:
                return fieldName.toLowerCase();
            case AUTO_DETECT:
                NamingConventionEnum detected = detectNamingConvention(fieldName);
                return normalizeToStandard(fieldName, detected);
            default:
                return fieldName.toLowerCase();
        }
    }

    /**
     * 从标准格式转换为目标格式
     */
    private static String convertFromStandard(String standardName, NamingConventionEnum toFormat) {
        switch (toFormat) {
            case SNAKE_CASE:
                return standardName;
            case UPPER_SNAKE_CASE:
                return standardName.toUpperCase();
            case CAMEL_CASE:
                return snakeToCamel(standardName, false);
            case PASCAL_CASE:
                return snakeToCamel(standardName, true);
            case KEBAB_CASE:
                return standardName.replace("_", "-");
            case LOWER_CASE:
                return standardName.replace("_", "");
            case UPPER_CASE:
                return standardName.replace("_", "").toUpperCase();
            default:
                return standardName;
        }
    }

    /**
     * 驼峰/帕斯卡命名转下划线命名
     */
    private static String camelToSnake(String camelCase) {
        if (StrUtil.isEmpty(camelCase)) {
            return camelCase;
        }

        StringBuilder result = new StringBuilder();
        for (int i = 0; i < camelCase.length(); i++) {
            char c = camelCase.charAt(i);
            if (Character.isUpperCase(c) && i > 0) {
                result.append('_');
                result.append(Character.toLowerCase(c));
            } else {
                result.append(Character.toLowerCase(c));
            }
        }
        return result.toString();
    }

    /**
     * 下划线命名转驼峰/帕斯卡命名
     */
    private static String snakeToCamel(String snakeCase, boolean pascalCase) {
        if (StrUtil.isEmpty(snakeCase)) {
            return snakeCase;
        }

        StringBuilder result = new StringBuilder();
        boolean capitalizeNext = pascalCase;

        for (char c : snakeCase.toCharArray()) {
            if (c == '_') {
                capitalizeNext = true;
            } else if (capitalizeNext) {
                result.append(Character.toUpperCase(c));
                capitalizeNext = false;
            } else {
                result.append(Character.toLowerCase(c));
            }
        }

        return result.toString();
    }

    /**
     * 转换Map中的所有字段名
     */
    public static Map<String, Object> convertMapKeys(Map<String, Object> originalMap, 
                                                    NamingConventionEnum fromFormat, 
                                                    NamingConventionEnum toFormat) {
        if (originalMap == null || originalMap.isEmpty() || fromFormat == toFormat) {
            return originalMap;
        }

        Map<String, Object> convertedMap = new HashMap<>();
        for (Map.Entry<String, Object> entry : originalMap.entrySet()) {
            String originalKey = entry.getKey();
            String convertedKey = convert(originalKey, fromFormat, toFormat);
            convertedMap.put(convertedKey, entry.getValue());
        }

        return convertedMap;
    }

    /**
     * 清空转换缓存（用于内存管理）
     */
    public static void clearCache() {
        conversionCache.clear();
        log.info("[NamingConverter] 转换缓存已清空");
    }
}